#include "yaffs/direct/yaffs_flashif2.h"

static int read_chunk(struct yaffs_dev *dev, int nand_chunk,
                      u8 *data, int data_len,
                      u8 *oob, int oob_len,
                      enum yaffs_ecc_result *ecc_result)
{
    int ret;
    struct rt_mtd_nand_rw_args rd;
    rt_device_t nand_dev;
    
    rd.page = nand_chunk;
    rd.data = data;
    rd.data_len = data_len;
    rd.spare = oob;
    rd.spare_len = oob_len;

    nand_dev = (rt_device_t)dev->driver_context;
    ret = rt_device_control(nand_dev, RT_MTD_CTRL_READ, &rd);

    if (ret == RT_MTD_EOK)
    {
        *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
    }
    else if (ret == -RT_MTD_EECC_CORRECT)
    {
        *ecc_result = YAFFS_ECC_RESULT_FIXED;
    }
    else if (ret == -RT_MTD_EECC)
    {
        *ecc_result = YAFFS_ECC_RESULT_UNFIXED;
    }

    return ret ? YAFFS_FAIL : YAFFS_OK;
}

static int write_chunk(struct yaffs_dev *dev, int nand_chunk,
                       const u8 *data, int data_len,
                       const u8 *oob, int oob_len)
{
    int ret;
    struct rt_mtd_nand_rw_args rd;
    rt_device_t nand_dev;

    rd.page = nand_chunk;
    rd.data = (rt_uint8_t *)data;
    rd.data_len = data_len;
    rd.spare = (rt_uint8_t *)oob;
    rd.spare_len = oob_len;

    nand_dev = (rt_device_t)dev->driver_context;
    ret = rt_device_control(nand_dev, RT_MTD_CTRL_WRITE, &rd);

    return ret? YAFFS_FAIL : YAFFS_OK;
}

static int erase_block(struct yaffs_dev *dev, int block_no)
{
    int ret;
    rt_device_t nand_dev;

    nand_dev = (rt_device_t)dev->driver_context;
    ret = rt_device_control(nand_dev, RT_MTD_CTRL_ERASE, &block_no);

    return ret ? YAFFS_FAIL : YAFFS_OK;
}

static int check_block(struct yaffs_dev *dev, int block_no)
{
    int ret;
    rt_device_t nand_dev;

    nand_dev = (rt_device_t)dev->driver_context;
    ret = rt_device_control(nand_dev, RT_MTD_CTRL_CHECK, &block_no);

    return ret ? YAFFS_FAIL : YAFFS_OK;
}

static int mark_badblock(struct yaffs_dev *dev, int block_no)
{
    rt_device_t nand_dev;

    nand_dev = (rt_device_t)dev->driver_context;
    rt_device_control(nand_dev, RT_MTD_CTRL_MARK, &block_no);

    return YAFFS_OK;
}

static int initialise(struct yaffs_dev *dev)
{
    return YAFFS_OK;
}

static int deinitialise(struct yaffs_dev *dev)
{
    return YAFFS_OK;
}

void yaffs_mtd_drv_install(struct yaffs_dev *dev)
{
    dev->drv.drv_read_chunk_fn   = read_chunk;
    dev->drv.drv_write_chunk_fn  = write_chunk;
    dev->drv.drv_erase_fn        = erase_block;
    dev->drv.drv_check_bad_fn    = check_block;
    dev->drv.drv_mark_bad_fn     = mark_badblock;
    dev->drv.drv_initialise_fn   = initialise;
    dev->drv.drv_deinitialise_fn = deinitialise;
}

int yaffs_start_up(void)
{
    static struct yaffs_dev *flash = RT_NULL;
    rt_mtd_nand_t mtd_nand;

    if (flash != RT_NULL)
    {
        return 0;
    }

    mtd_nand = (rt_mtd_nand_t)rt_device_find("root");
    if (mtd_nand == RT_NULL)
        return -1;

    if (mtd_nand->parent.type != RT_Device_Class_MTD)
        return -1;

    flash = (struct yaffs_dev *)rt_malloc(sizeof(struct yaffs_dev));
    rt_memset(flash, 0, sizeof(struct yaffs_dev));

    flash->param.name = "/root";
    flash->param.n_caches = 0;
    flash->param.start_block = mtd_nand->block_start;
    flash->param.end_block = mtd_nand->block_end;
    flash->param.total_bytes_per_chunk = mtd_nand->page_size;
    flash->param.spare_bytes_per_chunk = mtd_nand->oob_size;
    flash->param.chunks_per_block = mtd_nand->pages_per_block;
    flash->param.use_nand_ecc = 0;
    flash->param.is_yaffs2 = 1;
    flash->param.n_reserved_blocks = 4;
    flash->driver_context = mtd_nand;
    mtd_nand->priv = flash;

    yaffsfs_OSInitialisation();
    yaffs_mtd_drv_install(flash);
    yaffs_add_device(flash);

    return 0;
}
